package org.karelun.rabbit.rcp.editor;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.transaction.CDOTransactionConflictEvent;
import org.eclipse.emf.cdo.transaction.CDOTransactionFinishedEvent;
import org.eclipse.emf.cdo.transaction.CDOTransactionStartedEvent;
import org.eclipse.emf.cdo.util.CDOUtil;
import org.eclipse.emf.cdo.view.CDOView;
import org.eclipse.emf.cdo.view.CDOViewInvalidationEvent;
import org.eclipse.emf.common.command.BasicCommandStack;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.edit.EMFEditPlugin;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.net4j.util.event.IEvent;
import org.eclipse.net4j.util.event.IListener;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.karelun.ideaboard.gef.DefaultEditDomain;
import org.karelun.ideaboard.gef.EditPart;
import org.karelun.ideaboard.gef.editparts.ScalableFreeformRootEditPart;
import org.karelun.ideaboard.gef.palette.ConnectionCreationToolEntry;
import org.karelun.ideaboard.gef.palette.CreationToolEntry;
import org.karelun.ideaboard.gef.palette.MarqueeToolEntry;
import org.karelun.ideaboard.gef.palette.PaletteDrawer;
import org.karelun.ideaboard.gef.palette.PaletteEntry;
import org.karelun.ideaboard.gef.palette.PaletteGroup;
import org.karelun.ideaboard.gef.palette.PaletteRoot;
import org.karelun.ideaboard.gef.palette.SelectionToolEntry;
import org.karelun.ideaboard.gef.palette.ToolEntry;
import org.karelun.ideaboard.gef.ui.parts.GraphicalEditorWithFlyoutPalette;
import org.karelun.rabbit.bridge.ITransactionEditingDomain;
import org.karelun.rabbit.bridge.ITransactionEditingDomainProvider;
import org.karelun.rabbit.bridge.TransactionAdapterFactoryEditingDomain;
import org.karelun.rabbit.graph.Connection;
import org.karelun.rabbit.graph.Shape;
import org.karelun.rabbit.graph.provider.GraphItemProviderAdapterFactory;
import org.karelun.rabbit.rcp.editpart.PartFactory;

public class RabbitEditor extends GraphicalEditorWithFlyoutPalette implements ITransactionEditingDomainProvider,ISelectionProvider,ITabbedPropertySheetPageContributor{

	private CDOView cdoView;
	
	private ITransactionEditingDomain editingDomain;

	private ComposedAdapterFactory adapterFactory;
	
	private IPropertySheetPage propertySheetPage;
	
	private Collection<ISelectionChangedListener> selectionChangedListeners = new ArrayList<ISelectionChangedListener>();
	
	private ISelection editorSelection=StructuredSelection.EMPTY;
	
	private IListener cdoViewListener=new IListener() {
		public void notifyEvent(IEvent event) {
			if(event instanceof CDOViewInvalidationEvent){
			}else if(event instanceof CDOTransactionStartedEvent
				   ||event instanceof CDOTransactionFinishedEvent){
				cdoViewDirtyStateChanged();
			}else if(event instanceof CDOTransactionConflictEvent){
				
			}
		}
	};
	
	public RabbitEditor(){
		DefaultEditDomain ed = new DefaultEditDomain(this);
		ed.setCommandStack(new BasicCommandStack());
		setEditDomain(ed);
		ComposedAdapterFactory.Descriptor.Registry registry = EMFEditPlugin.getComposedAdapterFactoryDescriptorRegistry();
		adapterFactory = new ComposedAdapterFactory(registry);
		adapterFactory.addAdapterFactory(new ResourceItemProviderAdapterFactory());
		adapterFactory.addAdapterFactory(new GraphItemProviderAdapterFactory());
		adapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory());
	}
	
	@Override
	public void init(IEditorSite site, IEditorInput input)
			throws PartInitException {
		super.init(site, input);
		site.setSelectionProvider(this);
		createContents();
	}
	
	@Override
	protected void configureGraphicalViewer() {
		super.configureGraphicalViewer();
		
		ScalableFreeformRootEditPart rootEditPart = new ScalableFreeformRootEditPart();
		getGraphicalViewer().setRootEditPart(rootEditPart);
		getGraphicalViewer().setEditPartFactory(new PartFactory(getTransactionEditingDomain()));
	}
	
	@Override
	protected void initializeGraphicalViewer() {
		EObject content = editingDomain.getResourceSet().getResources().get(0).getContents().get(0);
		getGraphicalViewer().setContents(content);
		getGraphicalViewer().addSelectionChangedListener(new ISelectionChangedListener() {
			public void selectionChanged(SelectionChangedEvent event) {
				Object ele = ((IStructuredSelection)event.getSelection()).getFirstElement();
				Object model = ((EditPart)ele).getModel();
				setSelection(new StructuredSelection(model));
			}
		});
	}
	
	private void createContents() {
		IRabbitEditorInput editorInput=(IRabbitEditorInput)getEditorInput();
		cdoView=editorInput.getCDOView();
		BasicCommandStack commandStack = (BasicCommandStack)getEditDomain().getCommandStack();
		editingDomain=new TransactionAdapterFactoryEditingDomain(adapterFactory, commandStack,cdoView.getResourceSet(),cdoView);
		cdoView.addListener(cdoViewListener);
	}

	@Override
	protected PaletteRoot getPaletteRoot() {
		PaletteRoot root = new PaletteRoot();
		PaletteGroup toolGroup = new PaletteGroup("shapes");

		ToolEntry selectTool = new SelectionToolEntry();
		ToolEntry marqueTool = new MarqueeToolEntry();
		toolGroup.add(selectTool);
		toolGroup.add(marqueTool);

		root.setDefaultEntry(selectTool);
		root.add(toolGroup);

		root.add(createDrawer());
		return root;
	}

	private PaletteEntry createDrawer() {
		PaletteDrawer drawer = new PaletteDrawer("shape");
		CreationToolEntry shapeEntry = new CreationToolEntry("shape", "",
				new RabbitCreationFactory(Shape.class), null, null);
		drawer.add(shapeEntry);
		
		CreationToolEntry connEntry = new ConnectionCreationToolEntry("connection","", 
				new RabbitCreationFactory(Connection.class), null,null);
		drawer.add(connEntry);
		return drawer;
	}
	
	@Override
	protected void createActions() {
		super.createActions();
		
//		new RabbitDeleteAction()
		
	}
	
	@Override
	public boolean isDirty() {
		if(cdoView==null){
			return false;
		}
		return cdoView.isDirty();
	}
	
	@Override
	public void doSave(IProgressMonitor monitor) {
		final Map<Object,Object> saveOptions=new HashMap<Object, Object>();
		saveOptions.put(Resource.OPTION_SAVE_ONLY_IF_CHANGED, Resource.OPTION_SAVE_ONLY_IF_CHANGED_MEMORY_BUFFER);
		IRunnableWithProgress operation=new IRunnableWithProgress() {
			public void run(IProgressMonitor monitor) throws InvocationTargetException,
					InterruptedException {
				ResourceSet resourceSet = editingDomain.getResourceSet();
				EList<Resource> resources = CDOUtil.getResources(resourceSet);
				monitor.beginTask("", resources.size()); //$NON-NLS-1$
				for(Resource resource:resources){
					saveOptions.put(CDOResource.OPTION_SAVE_PROGRESS_MONITOR, new SubProgressMonitor(monitor, 1));
					saveOptions.put(CDOResource.OPTION_SAVE_OVERRIDE_TRANSACTION, cdoView);
					try {
						resource.save(saveOptions);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		};
		try {
			new ProgressMonitorDialog(getSite().getShell()).run(true, true, operation);
			((BasicCommandStack)editingDomain.getCommandStack()).saveIsDone();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void addSelectionChangedListener(ISelectionChangedListener listener) {
		selectionChangedListeners.add(listener);
	}

	@Override
	public ISelection getSelection() {
		return editorSelection;
	}

	@Override
	public void removeSelectionChangedListener(ISelectionChangedListener listener) {
		selectionChangedListeners.remove(listener);
	}

	@Override
	public void setSelection(ISelection selection) {
		editorSelection = selection;
	    for (ISelectionChangedListener listener : selectionChangedListeners){
	    	listener.selectionChanged(new SelectionChangedEvent(this, selection));
	    }
	}

	@Override
	public ITransactionEditingDomain getTransactionEditingDomain() {
		return editingDomain;
	}
	
	
	@Override
	public Object getAdapter(Class type) {
		if(type==IPropertySheetPage.class){
			if(propertySheetPage==null||propertySheetPage.getControl().isDisposed()){
				propertySheetPage=new TabbedPropertySheetPage(this);
				return propertySheetPage;
			}
		}else if(type==ITransactionEditingDomain.class){
			return editingDomain;
		}
		return super.getAdapter(type);
	}
	
	@Override
	public void dispose() {
		if(propertySheetPage!=null){
			propertySheetPage.dispose();
			propertySheetPage=null;
		}
		cdoView.removeListener(cdoViewListener);
		cdoView.close();
		adapterFactory.dispose();
		super.dispose();
	}
	
	private void cdoViewDirtyStateChanged() {
		getSite().getShell().getDisplay().asyncExec(new Runnable(){
			public void run() {
				firePropertyChange(IEditorPart.PROP_DIRTY);
			}
		});
	}

	@Override
	public String getContributorId() {
		return "org.karelun.rabbit.rcp.propertyContributor";
	}
}
